package com.niiwoo.civet.trade.service.local.intelligenceInvest;

import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.trade.constant.DatePatternConstant;
import com.niiwoo.civet.trade.constant.MqConstant;
import com.niiwoo.civet.trade.constant.RabbitConstant;
import com.niiwoo.civet.trade.dao.entity.AssetOrgProject;
import com.niiwoo.civet.trade.dao.entity.Project;
import com.niiwoo.civet.trade.dao.entity.ProjectInvesting;
import com.niiwoo.civet.trade.dao.entity.ProjectPackage;
import com.niiwoo.civet.trade.dao.entity.ProjectPackageDetail;
import com.niiwoo.civet.trade.dao.entity.ProjectPackageInvestOrder;
import com.niiwoo.civet.trade.dao.entity.ServicePublishConfig;
import com.niiwoo.civet.trade.dao.entity.ServiceTypeConfig;
import com.niiwoo.civet.trade.dao.mapper.AssetOrgProjectMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectInvestingMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectPackageDetailMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectPackageInvestOrderMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectPackageMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ServicePublishConfigMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ServiceTypeConfigMapperExt;
import com.niiwoo.civet.trade.dto.ProjectPackagePublishingDTO;
import com.niiwoo.civet.trade.dto.ServicePublishConfigDTO;
import com.niiwoo.civet.trade.dto.ServiceTypeConfigDTO;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.CancelPublishRequestDTO;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.DesignatedPackageRequestDTO;
import com.niiwoo.civet.trade.dto.request.intelligenceInvest.FirstPublishRequestDTO;
import com.niiwoo.civet.trade.enums.PackageInvestOrderEvolutionStatusEnum;
import com.niiwoo.civet.trade.enums.ProjectStatusEnum;
import com.niiwoo.civet.trade.enums.Intelligence.ProjectPackageStatusEnum;
import com.niiwoo.civet.trade.service.local.newRefund.dispatch.RefundValidateService;
import com.niiwoo.civet.trade.service.local.project.ProjectLocalService;
import com.niiwoo.tripod.base.utils.DateUtils;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class ProjectPackageLocalService {

    @Autowired
    private ServiceTypeConfigMapperExt serviceTypeConfigMapperExt;

    @Autowired
    private ServicePublishConfigMapperExt servicePublishConfigMapperExt;

    @Autowired
    private ProjectMapperExt projectMapperExt;

    @Autowired
    private ProjectPackageDetailMapperExt projectPackageDetailMapperExt;

    @Autowired
    private ProjectPackageMapperExt projectPackageMapperExt;

    @Autowired
    private ProjectPackageInvestOrderMapperExt projectPackageInvestOrderMapperExt;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private RefundValidateService validateService;

    @Autowired
    private IntelligenceInvestService intelligenceInvestService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ProjectInvestingMapperExt projectInvestingMapperExt;

    @Autowired
    private ProjectLocalService projectLocalService;

    @Autowired
    private AssetOrgProjectMapperExt assetOrgProjectMapperExt;

    /**
     * 指定打包
     *
     * @param requestDTO
     */
    @Transactional
    public void designatedPackage(DesignatedPackageRequestDTO requestDTO) {
        Assert.notNull(requestDTO, "designatedPackageRequestDTO must not be null");
        log.info("designatedPackage requestDTO:{}", JSON.toJSON(requestDTO));
        validateService.validate(requestDTO);

        ServiceTypeConfig serviceTypeConfig = serviceTypeConfigMapperExt.selectByPrimaryKey(requestDTO.getServiceConfigId());
        Assert.notNull(serviceTypeConfig, "serviceTypeConfig must not be null, serviceConfigId:" + requestDTO.getServiceConfigId());

        BigDecimal amount = BigDecimal.ZERO;
        for (Long projectId : requestDTO.getProjectIdSet()) {
            Project project = projectMapperExt.selectByPrimaryKey(projectId);
            Assert.notNull(project, "project must not be null, projectId:" + projectId);

            amount = amount.add(project.getContractAmount());
        }

        ServicePublishConfigDTO servicePublishConfigDTO = new ServicePublishConfigDTO();
        servicePublishConfigDTO.setServiceId(serviceTypeConfig.getId());
        servicePublishConfigDTO.setChannelCode(requestDTO.getPackagePublishChannelCode());
        servicePublishConfigDTO.setServiceLimit(amount);
        servicePublishConfigDTO.setUseableCouponAddRate(requestDTO.getBeUsableRaiseInterestPrize());
        servicePublishConfigDTO.setUseableRedPacketCashback(requestDTO.getBeUsableCashbackPrize());
        servicePublishConfigDTO.setPublishTime(requestDTO.getPublishTime());

        ServiceTypeConfigDTO serviceTypeConfigDTO = new ServiceTypeConfigDTO();
        BeanUtils.copyProperties(serviceTypeConfig, serviceTypeConfigDTO);

        ProjectPackage projectPackage = doPackage(serviceTypeConfigDTO, servicePublishConfigDTO, amount, Byte.valueOf("1"), requestDTO.getPackageManagerId());

        for (Long projectId : requestDTO.getProjectIdSet()) {
            ProjectPackageDetail projectPackageDetail = new ProjectPackageDetail();
            projectPackageDetail.setId(snowflakeIdWorker.nextId());
            projectPackageDetail.setPackageId(projectPackage.getPackageId());
            projectPackageDetail.setProjectId(projectId);
            projectPackageDetail.setCreateTime(new Date());
            projectPackageDetail.setUpdateTime(new Date());

            projectPackageDetailMapperExt.insertSelective(projectPackageDetail);
        }

        ProjectPackage updateRecord = new ProjectPackage();
        updateRecord.setPackageId(projectPackage.getPackageId());
        updateRecord.setStatus(ProjectPackageStatusEnum.WAITING_PUBLISH.getStatus());
        updateRecord.setUpdateTime(new Date());
        projectPackageMapperExt.updateByPrimaryKeySelective(updateRecord);

        log.info("designatedPackage successful projectPackage:{}", JSON.toJSON(projectPackage));

        // 打包完成后通知发布
        notifyPublish(projectPackage.getPackageId());
    }

    /**
     * 定时任务打包
     */
    @Transactional
    public void doPackageForJob() {
        log.info("定时打包...");
        if (!intelligenceInvestService.intelligenceInvestUsable()) {
            return;
        }
        // 查询出所有启用中的发布配置
        List<ServicePublishConfig> servicePublishConfigList = servicePublishConfigMapperExt.queryServicePublishConfigListByStatus(Byte.valueOf("1"));
        log.info("doPackageForJob servicePublishConfigList:{}", JSON.toJSON(servicePublishConfigList));
        servicePublishConfigList.forEach(s -> doPackageForConfig(s.getId()));
    }

    /**
     * 通过发布配置打包
     */
    @Transactional
    public void doPackageForConfig(Long packageConfigId) {
        if (!intelligenceInvestService.intelligenceInvestUsable()) {
            return;
        }
        ServicePublishConfig servicePublishConfig = servicePublishConfigMapperExt.selectByPrimaryKey(packageConfigId);
        log.info("doPackageForConfig servicePublishConfig:{}", JSON.toJSON(servicePublishConfig));
        Assert.notNull(servicePublishConfig, "servicePublishConfig must not be null, packageConfigId:" + packageConfigId);
        if (!Byte.valueOf("1").equals(servicePublishConfig.getStatus())) {
            log.info("打包配置状态为未启用，不需要再打包。packageConfigId:{},servicePublishConfig:{}", packageConfigId, JSON.toJSON(servicePublishConfig));
            return;
        }

        List<ProjectPackage> projectPackageList = projectPackageMapperExt.queryProjectPackageListByServiceConfigIdAndPackagePublishChannelCodeAndStatus(servicePublishConfig.getServiceId(), servicePublishConfig.getChannelCode(), ProjectPackageStatusEnum.PACKAGING.getStatus());
        if (!projectPackageList.isEmpty()) {
            log.info("有同一个服务配置的包正在打包中，不需要再打包。packageConfigId:{},projectPackageList.size:{}", packageConfigId, projectPackageList.size());
            return;
        }
        ServiceTypeConfig serviceTypeConfig = serviceTypeConfigMapperExt.selectByPrimaryKey(servicePublishConfig.getServiceId());

        projectPackageList = projectPackageMapperExt.queryProjectPackageListByParam(serviceTypeConfig.getChannelCode(),
                serviceTypeConfig.getDeadlineUnit(), serviceTypeConfig.getDeadline(), serviceTypeConfig.getInterestRate(),
                serviceTypeConfig.getRepaymentType(), servicePublishConfig.getChannelCode(), ProjectPackageStatusEnum.PACKAGING.getStatus());
        if (!projectPackageList.isEmpty()) {
            log.info("同一打包期限利率还款方式的标的包还有正在打包中。serviceTypeConfig:{},projectPackageList:{}", JSON.toJSON(serviceTypeConfig), JSON.toJSON(projectPackageList));
            return;
        }

        ServiceTypeConfigDTO serviceTypeConfigDTO = new ServiceTypeConfigDTO();
        BeanUtils.copyProperties(serviceTypeConfig, serviceTypeConfigDTO);

        ServicePublishConfigDTO servicePublishConfigDTO = new ServicePublishConfigDTO();
        BeanUtils.copyProperties(servicePublishConfig, servicePublishConfigDTO);

        doPackage(serviceTypeConfigDTO, servicePublishConfigDTO, BigDecimal.ZERO, Byte.valueOf("2"), null);
    }

    /**
     * 定时任务添加标的
     */
    public void addProjectForJob() {
        log.info("定时加标...");
        // 查询出最近一个月提交申请分端到智能投/新手智能投投资中并且未打包的标的
        List<Project> projectList = projectMapperExt.selectWaitingPackageProjectList();
        log.info("待打包标的 projectList:{}", JSON.toJSON(projectList));
        projectList.forEach(p -> addProject(p.getProjectId()));
    }

    /**
     * 添加标的
     *
     * @param projectId
     */
    public void addProject(Long projectId) {
        Project project = projectMapperExt.selectByPrimaryKey(projectId);
        Assert.notNull(project, "project must not be null, projectId:" + projectId);
        if(project.getInvestEndTime() != null){
            ((ProjectPackageLocalService) AopContext.currentProxy()).resetInvestEndTime(projectId);
        }

        List<ProjectPackage> projectPackageList = projectPackageMapperExt.queryProjectPackageListByParam(project.getChannelCode(),
                project.getDeadlineUnit(), project.getDeadline(), project.getBorrowRate(), project.getRepaymentType(), null, ProjectPackageStatusEnum.PACKAGING.getStatus());
        if (!projectPackageList.isEmpty()) {
            Long projectPackageId = projectPackageList.get(0).getPackageId();
            log.info("addProject projectPackageId:{},projectId:{}", projectPackageId, projectId);
            ((ProjectPackageLocalService) AopContext.currentProxy()).addProject(projectPackageList.get(0).getPackageId(), projectId);
        } else {
            log.error("标的对应的四要素打包中的包为空。projectId:{},project:{}", projectId, JSON.toJSON(project));
        }

    }

    @Transactional
    public void resetInvestEndTime(Long projectId){
        projectMapperExt.resetInvestEndTime(projectId);
        projectInvestingMapperExt.resetInvestEndTime(projectId);
        assetOrgProjectMapperExt.resetFundraisingEndTime(projectId);
    }

    /**
     * 加标
     *
     * @param projectPackageId
     * @param projectId
     */
    @Transactional
    public void addProject(Long projectPackageId, Long projectId) {
        ProjectPackageDetail projectPackageDetail = projectPackageDetailMapperExt.selectProjectPackageDetailByProjectId(projectId);
        if (projectPackageDetail != null) {
            log.warn("标的已经被打包。projectId:{},projectPackageDetail:{}", projectId, JSON.toJSON(projectPackageDetail));
            return;
        }

        ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(projectPackageId);

        Project project = projectMapperExt.selectByPrimaryKey(projectId);

        if(!projectLocalService.isZhiNengTou(project.getChannelCode())){
            log.warn("标的渠道非智能投。projectPackageId:{},project:{}", projectPackageId, JSON.toJSON(project));
            return;
        }

        // 修改包金额
        ProjectPackage updateRecord = new ProjectPackage();
        updateRecord.setPackageId(projectPackageId);
        updateRecord.setAmount(projectPackage.getAmount().add(project.getContractAmount()));
        updateRecord.setUpdateTime(new Date());
        projectPackageMapperExt.updateByPrimaryKeySelective(updateRecord);

        // 添加标的
        projectPackageDetail = new ProjectPackageDetail();
        projectPackageDetail.setId(snowflakeIdWorker.nextId());
        projectPackageDetail.setPackageId(projectPackageId);
        projectPackageDetail.setProjectId(projectId);
        projectPackageDetail.setCreateTime(new Date());
        projectPackageDetail.setUpdateTime(new Date());
        projectPackageDetailMapperExt.insertSelective(projectPackageDetail);


        //包实际金额达到服务额度
        if (updateRecord.getAmount().compareTo(projectPackage.getServiceLimit()) >= 0) {
            updateRecord = new ProjectPackage();
            updateRecord.setPackageId(projectPackageId);
            updateRecord.setStatus(ProjectPackageStatusEnum.WAITING_PUBLISH.getStatus());
            updateRecord.setUpdateTime(new Date());
            projectPackageMapperExt.updateByPrimaryKeySelective(updateRecord);

            //通知打包
            notifyPackage(projectPackage.getPackageConfigId());

            //通知发布
            notifyPublish(projectPackage.getPackageId());
        }
    }

    /**
     * 定时任务发布
     */
    public void doPublishForJob() {
        log.info("定时发布...");
        List<ProjectPackage> projectPackageList = projectPackageMapperExt.queryWaitingPublish();
        log.info("待发布包列表 projectPackageList:{}", JSON.toJSON(projectPackageList));
        List<ProjectPackagePublishingDTO> projectPackagePublishingDTOList = new ArrayList<>();

        for (ProjectPackage projectPackage : projectPackageList) {
            ProjectPackagePublishingDTO ProjectPackagePublishingDTO = new ProjectPackagePublishingDTO();
            BeanUtils.copyProperties(projectPackage, ProjectPackagePublishingDTO);


            projectPackagePublishingDTOList.add(ProjectPackagePublishingDTO);
        }

        Collections.sort(projectPackagePublishingDTOList);
        log.info("待发布包列表 排序后 projectPackagePublishingDTOList:{}", JSON.toJSON(projectPackagePublishingDTOList));

        ProjectPackageLocalService currentProxy = (ProjectPackageLocalService) AopContext.currentProxy();
        projectPackagePublishingDTOList.forEach(p -> currentProxy.doPublish(p.getPackageId()));
    }

    /**
     * 满标后通过服务类型配置发布
     */
    public void doPublishForFull(ProjectPackage projectPackage) {
        List<ProjectPackage> projectPackageList = projectPackageMapperExt.queryProjectPackageListByServiceConfigIdAndPackagePublishChannelCodeAndStatus(
                projectPackage.getServiceConfigId(), projectPackage.getPackagePublishChannelCode(), ProjectPackageStatusEnum.INVESTING.getStatus());
        List<ProjectPackagePublishingDTO> projectPackagePublishingDTOList = new ArrayList<>();

        for (ProjectPackage temp : projectPackageList) {
            ProjectPackagePublishingDTO ProjectPackagePublishingDTO = new ProjectPackagePublishingDTO();
            BeanUtils.copyProperties(temp, ProjectPackagePublishingDTO);


            projectPackagePublishingDTOList.add(ProjectPackagePublishingDTO);
        }

        Collections.sort(projectPackagePublishingDTOList);
        log.info("待发布包列表 排序后 projectPackagePublishingDTOList:{}", JSON.toJSON(projectPackagePublishingDTOList));

        ProjectPackageLocalService currentProxy = (ProjectPackageLocalService) AopContext.currentProxy();
        if(!projectPackagePublishingDTOList.isEmpty()){
            //相同服务类型与渠道的只需要发布一个就够
            currentProxy.doPublish(projectPackagePublishingDTOList.get(0).getPackageId());
        }
    }

    /**
     * 发布
     *
     * @param packageId
     */
    @Transactional
    public void doPublish(Long packageId) {
        if (!intelligenceInvestService.intelligenceInvestUsable()) {
            return;
        }
        ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(packageId);
        Assert.notNull(projectPackage, "projectPackage must not be null, packageId:" + packageId);
        if (!ProjectPackageStatusEnum.WAITING_PUBLISH.getStatus().equals(projectPackage.getStatus())) {
            log.info("标的包状态非待发布状态。packageId:{},projectPackage:{}", packageId, JSON.toJSON(projectPackage));
            return;
        }

        Long currentTimeMillis = System.currentTimeMillis();
        if (projectPackage.getFirstPublishTimeMillisecond() == null && projectPackage.getExpectPublishTimeMillisecond() > currentTimeMillis) {
            log.info("非优先发布，系统当前时间没到预计发布时间。packageId:{},projectPackage:{},currentTimeMillis:{}", packageId, JSON.toJSON(projectPackage), currentTimeMillis);
            return;
        }

        List<ProjectPackage> projectPackageList = projectPackageMapperExt.queryProjectPackageListByServiceConfigIdAndPackagePublishChannelCodeAndStatus(projectPackage.getServiceConfigId(), projectPackage.getPackagePublishChannelCode(), ProjectPackageStatusEnum.INVESTING.getStatus());
        if (!projectPackageList.isEmpty()) {
            log.info("同一打包配置的标的包还有正在投资中。serviceConfigId:{},projectPackageList:{}", projectPackage.getServiceConfigId(), JSON.toJSON(projectPackageList));
            return;
        }

        projectPackageList = projectPackageMapperExt.queryProjectPackageListByParam(projectPackage.getChannelCode(),
                projectPackage.getDeadlineUnit(), projectPackage.getDeadline(),
                projectPackage.getBorrowRate(), projectPackage.getRepaymentType(),
                projectPackage.getPackagePublishChannelCode(), ProjectPackageStatusEnum.INVESTING.getStatus());
        if (!projectPackageList.isEmpty()) {
            log.info("同一打包期限利率还款方式的标的包还有正在投资中。serviceConfigId:{},projectPackageList:{}", projectPackage.getServiceConfigId(), JSON.toJSON(projectPackageList));
            return;
        }

        if (BigDecimal.ZERO.compareTo(projectPackage.getAmount()) >= 0) {
            log.warn("待发布的包金额不大于0",JSON.toJSON(projectPackage));
            return;
        }
        log.info("doPublish projectPackage:{}", JSON.toJSON(projectPackage));

        ProjectPackage updateRecord = new ProjectPackage();
        updateRecord.setPackageId(packageId);
        updateRecord.setStatus(ProjectPackageStatusEnum.INVESTING.getStatus());
        updateRecord.setRealPublishTime(new Date());
        updateRecord.setInvestEndTime(DateUtils.addDaysReturnTime(updateRecord.getRealPublishTime(), projectPackage.getPackageConfigOverday()));
        updateRecord.setUpdateTime(new Date());
        projectPackageMapperExt.updateByPrimaryKeySelective(updateRecord);

        List<ProjectPackageDetail> projectPackageDetailList = projectPackageDetailMapperExt.selectProjectPackageDetailByPackageId(packageId);
        for (ProjectPackageDetail projectPackageDetail : projectPackageDetailList) {
            Project projectUpdateRecord = new Project();
            projectUpdateRecord.setProjectId(projectPackageDetail.getProjectId());
            projectUpdateRecord.setInvestEndTime(updateRecord.getInvestEndTime());
            projectUpdateRecord.setUpdateTime(new Date());
            projectMapperExt.updateByPrimaryKeySelective(projectUpdateRecord);

            ProjectInvesting projectInvestingUpdateRecord = new ProjectInvesting();
            projectInvestingUpdateRecord.setProjectId(projectPackageDetail.getProjectId());
            projectInvestingUpdateRecord.setInvestEndTime(updateRecord.getInvestEndTime());
            projectInvestingUpdateRecord.setUpdateTime(new Date());
            projectInvestingMapperExt.updateByPrimaryKeySelective(projectInvestingUpdateRecord);

            AssetOrgProject params = new AssetOrgProject();
            params.setProjectId(projectPackageDetail.getProjectId());
            params.setFundraisingEndTime(updateRecord.getInvestEndTime());
            assetOrgProjectMapperExt.updateByPrimaryKeySelective(params);
        }

    }

    @Transactional
    public void cancelPublish(CancelPublishRequestDTO requestDTO) {
        Assert.notNull(requestDTO, "cancelPublishRequestDTO must not be null");
        log.info("cancelPublish requestDTO:{}", JSON.toJSON(requestDTO));
        validateService.validate(requestDTO);

        ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(requestDTO.getPackageId());
        Assert.notNull(projectPackage, "projectPackage must not be null,packageId:" + requestDTO.getPackageId());
        if (!ProjectPackageStatusEnum.WAITING_PUBLISH.getStatus().equals(projectPackage.getStatus())
                && !ProjectPackageStatusEnum.PACKAGING.getStatus().equals(projectPackage.getStatus())) {
            log.info("标的包状态非待发布状态。packageId:{},projectPackage:{}", requestDTO.getPackageId(), JSON.toJSON(projectPackage));
            throw new BizException("TRD10041");
        }

        ProjectPackage updateRecord = new ProjectPackage();
        updateRecord.setPackageId(requestDTO.getPackageId());
        updateRecord.setStatus(ProjectPackageStatusEnum.MISCARRY.getStatus());
        updateRecord.setCancelPublishManagerId(requestDTO.getCancelManagerId());
        updateRecord.setUpdateTime(new Date());
        projectPackageMapperExt.updateByPrimaryKeySelective(updateRecord);

        List<ProjectPackageDetail> projectPackageDetailList = projectPackageDetailMapperExt.selectProjectPackageDetailByPackageId(requestDTO.getPackageId());
        for (ProjectPackageDetail projectPackageDetail : projectPackageDetailList) {
            projectPackageDetailMapperExt.deleteByPrimaryKey(projectPackageDetail.getId());
        }
    }

    @Transactional(propagation = Propagation.MANDATORY, isolation = READ_COMMITTED)
    public void resetProjectChannelCode(Long projectId) {
        Project project = projectMapperExt.selectByPrimaryKey(projectId);
        ProjectPackageDetail projectPackageDetail = projectPackageDetailMapperExt.selectProjectPackageDetailByProjectId(projectId);
        if(projectPackageDetail != null){
            ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(projectPackageDetail.getPackageId());
            log.info("resetProjectChannelCode project:{}, projectPackage:{}", JSON.toJSON(project), JSON.toJSON(projectPackage));
            if (!ProjectPackageStatusEnum.WAITING_PUBLISH.getStatus().equals(projectPackage.getStatus()) &&
                    !ProjectPackageStatusEnum.PACKAGING.getStatus().equals(projectPackage.getStatus())) {
                log.info("标的包状态非待发布状态。projectId:{},projectPackage:{}", projectId, JSON.toJSON(projectPackage));
                throw new BizException("TRD10042");
            }

            ProjectPackage updateRecord = new ProjectPackage();
            updateRecord.setPackageId(projectPackage.getPackageId());
            updateRecord.setAmount(projectPackage.getAmount().subtract(project.getContractAmount()));
            if(BigDecimal.ZERO.compareTo(updateRecord.getAmount()) >= 0){
                updateRecord.setStatus(ProjectPackageStatusEnum.MISCARRY.getStatus());
            }
            updateRecord.setUpdateTime(new Date());
            projectPackageMapperExt.updateByPrimaryKeySelective(updateRecord);

            projectPackageDetailMapperExt.deleteByPrimaryKey(projectPackageDetail.getId());
        }
    }

    /**
     * 优先发布
     *
     * @param requestDTO
     */
    public void firstPublish(FirstPublishRequestDTO requestDTO) {
        Assert.notNull(requestDTO, "firstPublishRequestDTO must not be null");
        log.info("firstPublish requestDTO:{}", JSON.toJSON(requestDTO));
        validateService.validate(requestDTO);

        ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(requestDTO.getPackageId());
        Assert.notNull(projectPackage, "projectPackage must not be null,packageId:" + requestDTO.getPackageId());

        if (!ProjectPackageStatusEnum.WAITING_PUBLISH.getStatus().equals(projectPackage.getStatus())) {
            log.info("标的包状态非待发布状态。packageId:{},projectPackage:{}", requestDTO.getPackageId(), JSON.toJSON(projectPackage));
            return;
        }

        ProjectPackage updateRecord = new ProjectPackage();
        updateRecord.setPackageId(requestDTO.getPackageId());
        updateRecord.setFirstPublishManagerId(requestDTO.getFirstPublishManagerId());
        updateRecord.setFirstPublishTimeMillisecond(System.currentTimeMillis());
        updateRecord.setUpdateTime(new Date());
        projectPackageMapperExt.updateByPrimaryKeySelective(updateRecord);
    }

    @Transactional(isolation = READ_COMMITTED)
    public boolean isPackagedProject(Long projectId){
        Long packageId = projectPackageDetailMapperExt.selectPackageIdByProjectId(projectId);
        return  packageId != null;
    };

    /**
     * 满标处理
     *
     * @param projectId
     */
    @Transactional
    public void projectFull(Long projectId) {
        ProjectPackageDetail projectPackageDetail = projectPackageDetailMapperExt.selectProjectPackageDetailByProjectId(projectId);
        if (projectPackageDetail == null) {
            log.warn("标的不属于任何标的包。projectId:{}", projectId);
            return;
        }
        // 锁包处理，针对该包中有投资中的标的正在流标处理
        ProjectPackage projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(projectPackageDetail.getPackageId());

        List<Long> projectIdList = projectPackageDetailMapperExt.selectProjectIdByPackageId(projectPackageDetail.getPackageId());
        List<Project> projectList = projectMapperExt.queryProjectListByProjectIds(projectIdList);
        boolean flag = true;
        for (Project project : projectList) {
            if (ProjectStatusEnum.COLLECTING.getStatus().equals(project.getStatus())) {
                flag = false;
                log.warn("标的包中的标的还有正在投资中。projectId:{}，投资中标的project:{}", projectId, JSON.toJSON(project));
                break;
            }
        }

        if (!flag) {
            return;
        }

        // 修改所有包投资订单衍生状态
        List<ProjectPackageInvestOrder> projectPackageInvestOrderList = projectPackageInvestOrderMapperExt.selectTotalNumSuccessInvest(projectPackageDetail.getPackageId());
        for (ProjectPackageInvestOrder projectPackageInvestOrder : projectPackageInvestOrderList) {
            if (PackageInvestOrderEvolutionStatusEnum.INVESTING.getValue().equals(projectPackageInvestOrder.getEvolutionStatus())) {
                ProjectPackageInvestOrder updateRecord = new ProjectPackageInvestOrder();
                updateRecord.setId(projectPackageInvestOrder.getId());
                updateRecord.setEvolutionStatus(PackageInvestOrderEvolutionStatusEnum.BACKING.getValue());
                updateRecord.setUpdateTime(new Date());
                projectPackageInvestOrderMapperExt.updateByPrimaryKeySelective(updateRecord);
            }
        }
		// 调用智能投订单
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
			@Override
			public void afterCompletion(int status) {
				if (status == TransactionSynchronizationAdapter.STATUS_COMMITTED) {
					for (ProjectPackageInvestOrder projectPackageInvestOrder : projectPackageInvestOrderList) {
						if (PackageInvestOrderEvolutionStatusEnum.INVESTING.getValue().equals(projectPackageInvestOrder.getEvolutionStatus())) {
							rabbitTemplate.convertAndSend(RabbitConstant.Exchange.INTELLIGENCE_INVEST_END, RabbitConstant.RoutingKey.INTELLIGENCE_INVEST_END, projectPackageInvestOrder.getId());

						}
					}
				}
			}
		});

        // 该包已满，通知发布
        rabbitTemplate.convertAndSend(MqConstant.PROJECT_PACKAGE_PUBLISHING_FOR_FULL_EXCHANGE, MqConstant.PROJECT_PACKAGE_PUBLISHING_FOR_FULL_ROUTING, projectPackage);
    }

    /**
     * 通知打包
     *
     * @param packageConfigId
     */
    private void notifyPackage(Long packageConfigId) {
        rabbitTemplate.convertAndSend(MqConstant.PROJECT_PACKAGE_PACKAGING_EXCHANGE, MqConstant.PROJECT_PACKAGE_PACKAGING_ROUTING, packageConfigId);
    }

    /**
     * 通知发布
     *
     * @param packageId
     */
    private void notifyPublish(Long packageId) {
        rabbitTemplate.convertAndSend(MqConstant.PROJECT_PACKAGE_PUBLISHING_EXCHANGE, MqConstant.PROJECT_PACKAGE_PUBLISHING_ROUTING, packageId);
    }

    private ProjectPackage doPackage(ServiceTypeConfigDTO serviceTypeConfigDTO, ServicePublishConfigDTO servicePublishConfigDTO, BigDecimal amount, Byte packageWay, Long packageManagerId) {
        log.info("doPackage serviceTypeConfigDTO:{}, servicePublishConfigDTO:{}, amount:{}, packageWay:{}, packageManagerId:{}", JSON.toJSON(serviceTypeConfigDTO), JSON.toJSON(servicePublishConfigDTO), amount, packageWay, packageManagerId);
        String title = (Byte.valueOf("2").equals(serviceTypeConfigDTO.getServiceType()) ? "新手智能投 "  :"智能投 ") + DateUtils.format(new Date(), "yyMMdd");
        Long dayPackageCount = stringRedisTemplate.boundValueOps("PROJECT_PACKAGE_PACKAGED_DAY_" + FastDateFormat.getInstance(DatePatternConstant.YYYYMMDD).format(new Date())).increment(1);
        String dayIncrement = StringUtils.leftPad(dayPackageCount + "", 3, "0");
        title = title + dayIncrement;

        String packageNo = "ZNT" + DateUtils.format(new Date(), "yyMMdd") + serviceTypeConfigDTO.getDeadline() + (Byte.valueOf("1").equals(serviceTypeConfigDTO.getDeadlineUnit()) ? "M" : "D");
        packageNo = packageNo + serviceTypeConfigDTO.getRepaymentType().intValue();
        Long packageCount = stringRedisTemplate.boundValueOps("PROJECT_PACKAGE_PACKAGED").increment(1);
        String increment = StringUtils.leftPad(packageCount + "", 5, "0");
        packageNo = packageNo + increment;

        ProjectPackage projectPackage = new ProjectPackage();
        projectPackage.setPackageId(snowflakeIdWorker.nextId());
        projectPackage.setChannelCode(serviceTypeConfigDTO.getChannelCode());
        projectPackage.setServiceConfigId(servicePublishConfigDTO.getServiceId());
        projectPackage.setServiceName(serviceTypeConfigDTO.getServiceName());
        projectPackage.setPackageConfigId(servicePublishConfigDTO.getId());
        projectPackage.setPackageNo(packageNo);
        projectPackage.setPackageType(serviceTypeConfigDTO.getServiceType());
        projectPackage.setPackagePublishChannelCode(servicePublishConfigDTO.getChannelCode());
        projectPackage.setTitle(title);
        projectPackage.setDeadline(serviceTypeConfigDTO.getDeadline());
        projectPackage.setDeadlineUnit(serviceTypeConfigDTO.getDeadlineUnit());
        projectPackage.setBorrowRate(serviceTypeConfigDTO.getInterestRate());
        projectPackage.setRepaymentType(serviceTypeConfigDTO.getRepaymentType());
        projectPackage.setServiceLimit(servicePublishConfigDTO.getServiceLimit());
        projectPackage.setAmount(amount);
        projectPackage.setMinInvestAmount(serviceTypeConfigDTO.getMinInvest());
        projectPackage.setInvestIncrementAmount(serviceTypeConfigDTO.getIncrementalInvest());
        projectPackage.setInvestedAmount(BigDecimal.ZERO);
        projectPackage.setExpectPublishTime(servicePublishConfigDTO.getPublishTime() == null ? new Date() : servicePublishConfigDTO.getPublishTime());
        projectPackage.setExpectPublishTimeMillisecond(projectPackage.getExpectPublishTime().getTime());
        projectPackage.setPackageWay(packageWay);
        projectPackage.setPackageManagerId(packageManagerId);
        projectPackage.setStatus(ProjectPackageStatusEnum.PACKAGING.getStatus());
        projectPackage.setBeUsableRaiseInterestPrize(servicePublishConfigDTO.getUseableCouponAddRate());
        projectPackage.setBeUsableCashbackPrize(servicePublishConfigDTO.getUseableRedPacketCashback());
        projectPackage.setPackageConfigOverday(serviceTypeConfigDTO.getRaiseTime());
        projectPackage.setActivityRaiseRate(serviceTypeConfigDTO.getAddInterestRate());
        projectPackage.setActivityLabel(serviceTypeConfigDTO.getActivityLabel());
        projectPackage.setCreateTime(new Date());
        projectPackage.setUpdateTime(new Date());

        projectPackageMapperExt.insertSelective(projectPackage);
        log.info("doPackage projectPackage:{}", JSON.toJSON(projectPackage));
        return projectPackage;
    }
}
